/*
 * Copyright 2010 Christoph Widulle (christoph.widulle@googlemail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */



package com.android1.amarena2d.texture;

import com.android1.amarena2d.commons.Constants;
import com.android1.amarena2d.engine.EngineObject;
import com.android1.amarena2d.engine.IsLoadable;
import com.android1.amarena2d.engine.Resource;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;

import java.util.List;

public class AtlasMesh extends EngineObject implements IsLoadable, Resource,Resource.Consumer {

    protected final Resource.Manager resourceManager = new Resource.Manager(this);

    private final ManagedTexture managedTexture;
    private final TextureAtlas textureAtlas;

    private int maxMeshs;
    private AtlasTexturedMesh[] meshList;
    private Mesh mesh;
    private boolean dirty;
    private boolean init;


    public AtlasMesh(TextureAtlas textureAtlas) {
        this.textureAtlas = textureAtlas;
        this.managedTexture = textureAtlas.getManagedTexture();

        this.createFrames();

    }

    public boolean isInit() {
        return init;
    }

    public void init() {
        if (!isInit()) {
            this.init = true;
            this.managedTexture.obtainRef(this);
            this.managedTexture.init();
            this.textureAtlas.obtainRef(this);
            this.textureAtlas.init();
            createMesh();
            updateMesh();

            resourceManager.init();
        }
    }

    public void dispose() {
        if (canDispose()) {
            this.managedTexture.releaseRef(this);
            this.managedTexture.dispose();
            this.textureAtlas.releaseRef(this);
            this.managedTexture.dispose();
            mesh.dispose();
            mesh = null;
            resourceManager.dispose();
        }
    }

    protected boolean canDispose() {
        return init && resourceManager.canDispose();
    }

    protected void createMesh() {
        this.mesh = new Mesh(false, 6 * 4 * maxMeshs, 0,
                new VertexAttribute(VertexAttributes.Usage.Position, 2, "a_position"),
                new VertexAttribute(VertexAttributes.Usage.TextureCoordinates, 2, "a_texCoord"));
    }


    public AtlasTexturedMesh[] getAtlasTexturedMeshs() {
        return meshList;
    }

    public AtlasTexturedMesh getAtlasTexturedMesh(int index) {
        return meshList[index];
    }

    public void render(int index) {

        if (mesh == null) {
            init();
        }
        if (dirty)
            updateMesh();

        managedTexture.bind();
        mesh.render(GL10.GL_TRIANGLES, index * Constants.MESH_VERTICS_COUNT, Constants.MESH_VERTICS_COUNT);

    }

    protected void createFrames() {

        this.maxMeshs = textureAtlas.getTextureFrames().size();
        this.meshList = new AtlasTexturedMesh[maxMeshs];

        final List<AtlasTextureFrame> frames = textureAtlas.getTextureFrames();

        for (int i = 0; i < maxMeshs; i++) {
            AtlasTextureFrame frame = frames.get(i);

            AtlasTexturedMesh texturedMesh = new AtlasTexturedMeshImpl(this, frame);
            meshList[i] = texturedMesh;

        }
    }


    protected void updateMesh() {

        final float[] vertices = new float[maxMeshs * Constants.MESH_FLOAT_COUNT];
        int index = 0;

        for (int i = 0; i < meshList.length; i++) {
            final AtlasTexturedMesh mesh = meshList[i];


            if (!(mesh.getTextureFrame().getWidth() > 0 && mesh.getTextureFrame().getHeight() > 0)) {
                mesh.getTextureFrame().set(0, 0, managedTexture.getTexture().getWidth(), managedTexture.getTexture().getHeight());
            }
            if (mesh.getSize().isZero())
                mesh.getSize().set(mesh.getTextureFrame().getWidth(), mesh.getTextureFrame().getHeight());

            float u = mesh.getTextureFrame().getX() * managedTexture.getInvTexWidth();
            float v = mesh.getTextureFrame().getY() * managedTexture.getInvTexHeight();
            float u2 = (mesh.getTextureFrame().getX() + mesh.getTextureFrame().getWidth()) * managedTexture.getInvTexWidth();
            float v2 = (mesh.getTextureFrame().getY() + mesh.getTextureFrame().getHeight()) * managedTexture.getInvTexHeight();

            final float x = mesh.getX();
            final float y = mesh.getY() + mesh.getHeight();
//            final float y = mesh.getY() + mesh.getHeight();

            final float fx2 = x + mesh.getWidth();
            final float fy2 = y - mesh.getHeight();
//            final float fy2 = y - mesh.getHeight();


            if (mesh.isFlipX()) {
                float tmp = u;
                u = u2;
                u2 = tmp;
            }

            if (mesh.isFlipY()) {
                float tmp = v;
                v = v2;
                v2 = tmp;
            }


            vertices[index++] = x;
            vertices[index++] = y;
            vertices[index++] = u;
            vertices[index++] = v;

            vertices[index++] = x;
            vertices[index++] = fy2;
            vertices[index++] = u;
            vertices[index++] = v2;

            vertices[index++] = fx2;
            vertices[index++] = fy2;
            vertices[index++] = u2;
            vertices[index++] = v2;

            vertices[index++] = fx2;
            vertices[index++] = fy2;
            vertices[index++] = u2;
            vertices[index++] = v2;

            vertices[index++] = fx2;
            vertices[index++] = y;
            vertices[index++] = u2;
            vertices[index++] = v;

            vertices[index++] = x;
            vertices[index++] = y;
            vertices[index++] = u;
            vertices[index++] = v;


        }
        this.mesh.setVertices(vertices);
    }


    @Override
    public void obtainRef(Consumer consumer) {
        resourceManager.obtainRef(consumer);
    }

    @Override
    public void releaseRef(Consumer consumer) {
        resourceManager.releaseRef(consumer);
    }

    @Override
    public void releaseAllRefs() {
        resourceManager.releaseAllRefs();
    }

    @Override
    public boolean hasRefs() {
        return resourceManager.hasRefs();
    }

    @Override
    public void register(StateListener listener) {
        resourceManager.register(listener);
    }

    @Override
    public void unregister(StateListener listener) {
        resourceManager.unregister(listener);
    }

    @Override
    public void markPersistent() {
        resourceManager.markPersistent();
    }

    @Override
    public void revokePersistent() {
        resourceManager.revokePersistent();
    }

    @Override
    public boolean isEagerDispose() {
        return resourceManager.isEagerDispose();
    }

    @Override
    public void setEagerDispose(boolean dispose) {
        resourceManager.setEagerDispose(dispose);
    }

    @Override
    public boolean equalResource(Resource otherResource) {
        return this == otherResource;
    }

    public TextureAtlas getTextureAtlas() {
        return textureAtlas;
    }

    public ManagedTexture getManagedTexture() {
        return managedTexture;
    }

    public void setDirty() {
        dirty = true;
    }

    public boolean isDirty() {
        return dirty;
    }
}